// -*- indent-tabs-mode: nil -*-
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Web;

namespace seanfoy.oopsnet {
    public class Bugzilla {
	public class Triple {
	    public string first;
	    public string second;
	    public string third;
	    public Triple(string first, string second, string third) {
		this.first = first;
		this.second = second;
		this.third = third;
	    }
	}

	public static void addBQTriple(IDictionary d, int i, int j, Triple t) {
	    string tid = String.Format("0-{0}-{1}", i, j);
	    d["field" + tid] = t.first;
	    d["type" + tid] = t.second;
	    d["value" + tid] = t.third;
	}

	public static void addCNF(IDictionary dict, IEnumerable conjunction) {
	    int i = 0;
	    foreach (object c in conjunction) {
		if (c is Triple) {
		    addBQTriple(dict, i, 0, (Triple)c);
		}
		else {
		    int j = 0;
		    foreach (object d in (IEnumerable)c) {
			addBQTriple(dict, i, j, (Triple)d);
			++j;
		    }
		}
		++i;
	    }
	}

	/// <remarks>
	/// Thanks to the <c>params</c> keyword, this works
	/// even on singletons, unlike the ArrayList
	/// constructor itself. The ArrayList constructor's
	/// non-use of the params keyword may be justified
	/// by use cases such as singleton lists of
	/// strings, where a string is a sequence of chars,
	/// but for BZ purposes this is better.
	/// </remarks>
	public static ArrayList listify(params object [] args) {
	    return new ArrayList(args);
	}

	public static String querystring(IDictionary triples) {
	    StringBuilder sb = new StringBuilder();
	    foreach (DictionaryEntry p in triples) {
		sb.AppendFormat(
                    "{0}={1}&",
		    HttpUtility.UrlEncode((string)p.Key),
		    HttpUtility.UrlEncode((string)p.Value));
	    }
	    Potpourri.TrimEnd(sb, "&");
	    return sb.ToString();
	}

	//conj(dis(prop(f, o, v), prop(f, o, v)))
	public static IEnumerable makeCNF(Exception e, Uri requestUrl, NameValueCollection appSettings) {
            if (e == null) return null;

            ArrayList l =
		listify(
		    new Triple("short_desc", "casesubstring", e.GetType().FullName),
		    listify(
			    new Triple("bug_file_loc", "casesubstring", requestUrl.ToString()),
			    new Triple("short_desc", "anywords", e.Message)));
            if (appSettings["seanfoy.oopsnet.bzProduct"] != null) {
                l.Add(
                    new Triple("product", "equals", appSettings["seanfoy.oopsnet.bzProduct"]));
            }
            return l;
	}

        static void makeHTML(IDictionary pairs, Triple clause) {
            if (clause == null) return;
            if ("equals substring casesubstring anywordssubstr allwordssubstr anywords allwords".IndexOf(clause.second) == -1) {
                return;
            }
            if ("equals".Equals(clause.second)) pairs[clause.first] = null;
            pairs[clause.first] = Potpourri.setDefault(pairs, clause.first, newAL);
            ((ArrayList)pairs[clause.first]).Add(clause.third);
        }

        /// <summary>
        /// Satisfy cnf using as much of the input as
        /// possible and render the result to form
        /// controls suitable for BZ enter_bug processing
        /// </summary>
        public static String makeHTML(IEnumerable cnf) {
            if (cnf == null) return String.Empty;
            ListDictionary pairs = new ListDictionary();
            foreach (object clause in cnf) {
                ArrayList dis = clause as ArrayList;
                if (dis == null) {
                    makeHTML(pairs, clause as Triple);
                }
                else {
                    foreach (object c in dis) {
                        makeHTML(pairs, c as Triple);
                    }
                }
            }
            StringBuilder sb = new StringBuilder();
            foreach (DictionaryEntry p in pairs) {
                sb.AppendFormat(
                    "<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\n",
                    HttpUtility.HtmlEncode((String)p.Key),
                    HttpUtility.HtmlEncode(Potpourri.Join(" ", (ArrayList)p.Value)));
            }
            return sb.ToString();
        }

        public static IDictionary queryargs(IEnumerable cnf) {
	    ListDictionary d = new ListDictionary();
	    d["query_format"] = "advanced";
	    d["ctype"] = "rdf";
	    d["order"] = "Importance";
	    d["cmdtype"] = "doit";
	    addCNF(d, cnf);
            return d;
        }

	public static String queryurl(string baseUrl, IEnumerable cnf) {
	    return baseUrl + "?" + querystring(queryargs(cnf));
	}

        static object newAL_method() {
            return new ArrayList();
        }
        static Potpourri.MakeDefault newAL = new Potpourri.MakeDefault(newAL_method);
    }
}
